package com.obixlabs.security.credentialsstore.server;

import java.io.IOException;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.Properties;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;

public class SecurityServer
{
	private static final Logger logger = 
		LoggerFactory.getLogger(SecurityServer.class);
	
	private static final int ERROR_RETURN_CODE = 1;
	
	private static final long SERVER_KEEP_ALIVE_MESSAGE_INTERVAL = 1000 * 60 * 5;
	
	private static final String SPRING_CONFIG_CMD_LINE_ARG_KEY = "springconfig";
	private static final String APP_PROPERTIES_CMD_LINE_ARG_KEY = "props";
	
	private String[] CORE_BEANS = {	"userManagementServiceRMIExporter",									
									"groupManagementServiceRMIExporter",
									"permissionsManagementServiceRMIExporter",
									"webSSOServiceRMIExporter",
									"webServicePublisher"};
	
	public SecurityServer(	String springConfigFile,
							String applicationPropertiesFile)
							throws IOException
	{
		 
		BeanFactory beanFactory= 
			loadApplicationSpringContext(springConfigFile, 
										applicationPropertiesFile);
		
		//assert that all core beans are not null
		logger.info("Asserting presence of core application beans...");
		Object beanReference;
		for (String beanName : CORE_BEANS)
		{
			logger.info("Checking for bean '" + beanName + "'.");
			beanReference = beanFactory.getBean(beanName);
			assert(beanReference!=null);
		}

	}
	
	public static void main(String[] args)
	{
		CommandLine commandLine = parseAndValidateArgs(args);
		if (commandLine!=null)
		{
			String springConfigFile =
					commandLine.getOptionValue(SPRING_CONFIG_CMD_LINE_ARG_KEY);
			String applicationProperties = 
					commandLine.getOptionValue(APP_PROPERTIES_CMD_LINE_ARG_KEY);
			
			try
			{
				SecurityServer server = new SecurityServer(	springConfigFile, 
														applicationProperties);
				logger.info("Server started successfully! Now open for business.");
				while (true)
				{
					Thread.sleep(SERVER_KEEP_ALIVE_MESSAGE_INTERVAL);					
					logger.info(server.formatKeepAliveMessage());
				}
			}
			catch (IOException ioException)
			{
				logger.error(	"Unable to start security server due to an unexpected I/O Error! " +
								"Bailing Out...", 
								ioException);
				System.exit(1);
			}
			catch (InterruptedException interruptedExce)
			{
				logger.error("Server cycle interrupted. Falling through to server exit ...");				
			}
		}		
	}
	
	private BeanFactory loadApplicationSpringContext(
				String springConfigFile,
				String applicationPropertiesFile) 
				throws IOException
	{
		XmlBeanFactory result;
		
		logger.info("Attempting to spring context from '"+ 
			 		springConfigFile + "'.");
		
		FileSystemResource springApplicationFile=
			new FileSystemResource(springConfigFile);
		
		result = new XmlBeanFactory(springApplicationFile);
		
		Properties applicationProperties = 
				loadApplicationProperties(applicationPropertiesFile);
		
		PropertyPlaceholderConfigurer beanFactoryConfigurer=
			new PropertyPlaceholderConfigurer();
		beanFactoryConfigurer.setProperties(applicationProperties);
		
		beanFactoryConfigurer.postProcessBeanFactory(result);
		
		return result;
	}
	
	private Properties loadApplicationProperties(
				String applicationPropertiesFile) throws IOException
	{
		Properties result = new Properties();
		
		logger.info("Attempting to load application properties from '"+ 
				 	applicationPropertiesFile + "'.");
		
		FileSystemResource resource = 
			new FileSystemResource(applicationPropertiesFile);
		result.load(resource.getInputStream());
		return result;
	}

	
	private static CommandLine parseAndValidateArgs(String[] args)
	{
		CommandLine result;
		
		Options commandLineOptions = buildOptions();
		try
		{			
			CommandLineParser parser = new GnuParser();			
			result = parser.parse(commandLineOptions, args);
			
			if (!result.hasOption(SPRING_CONFIG_CMD_LINE_ARG_KEY) || 
				!result.hasOption(APP_PROPERTIES_CMD_LINE_ARG_KEY))
			{
				printUsage(commandLineOptions);
				System.exit(ERROR_RETURN_CODE);
			}
		}
		catch (ParseException parseException)
		{
			logger.error("Unable to parse command line arguments.", 
							parseException);
			printUsage(commandLineOptions);
			result = null;
		}
		
		return result;
	}

	
	private static final Options buildOptions()
	{
		Options result = new Options();
		
		result = 
		result	.addOption(new Option(SPRING_CONFIG_CMD_LINE_ARG_KEY,true,
							"path to spring context file."))
				.addOption(new Option(APP_PROPERTIES_CMD_LINE_ARG_KEY, true,
							"path to application properties."));
		
		return result;
	}
	
	private String formatKeepAliveMessage()
	{
		StringBuffer result = new StringBuffer();

		Date currentTime = new Date(System.currentTimeMillis());
		result.append("SecurityServer keep-alive message. Time=");
		result.append(SimpleDateFormat.getDateTimeInstance().format(currentTime));
		result.append("\n\t\t\t\t\t\t\t\t\tFree memory="
				+ (Runtime.getRuntime().freeMemory() / 100000) + "MB");
		result.append(", Total memory="
				+ (Runtime.getRuntime().totalMemory() / 100000) + "MB");

		return result.toString();
	}
	 
	private static void printUsage(Options options)
	{
		HelpFormatter helpFormatter = new HelpFormatter();
		helpFormatter.printHelp(
				"<java> com.obixlabs.red.security.SecurityServer", 
				options);
	} 	
}